<html>

<head>
    <meta charset="UTF-8">
    <title>应用模型信息修改</title>

    <link id="easyuiTheme"  href="../../static/easyui/themes/bootstrap/easyui.css" rel="stylesheet" type="text/css"/>
    <link href="../../static/easyui/themes/icon.css" rel="stylesheet" type="text/css"/>
    <link href="../../static/css/esaas-easyui-add.css" rel="stylesheet" type="text/css"/>
    <script src="../../static/easyui/jquery.min.js" type="text/javascript"></script>
    <script src="../../static/easyui/jquery.cookie.js" type="text/javascript"></script>
    <script src="../../static/easyui/ui-theme.js" type="text/javascript"></script>
    <script src="../../static/easyui/jquery.easyui.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../../static/easyui/locale/easyui-lang-zh_CN.js" type="text/javascript"></script>
    <script src="../../static/plugin/assets/jslib.util.js" type="text/javascript"></script>
    <script src="../../static/plugin/assets/jslib.jquery.ajax.js" type="text/javascript"></script>
    <script src="../../static/plugin/assets/jslib.easyui.util.js" type="text/javascript"></script>
    <script src="../../ffres/dict/all.js" type="text/javascript"></script>
    <a href="" target="_blank" id="commonLink" name="commonLink"></a>

    <script src="../../static/js/konva.min.js" type="text/javascript"></script>
    <script src="../../static/js/uuid.v4.js" type="text/javascript"></script>
    <script src="../../static/js/flowchart.js" type="text/javascript"></script>
    <style>
        .tool-panel {
            text-align:left;
            background: #f8f8f8;
            border-bottom: solid 1px #d4d4d4;
        }
        .node-panel {
            margin: auto 5px;
            text-align: center;
        }
        .node-panel .node-drop {
            padding: 5px 15px;
            font-size: 16px;
            background-color: #f6f7ff;
            border: solid 2px #04679a;
            margin: 15px auto;
            width: 170px;
            height: 22px;
        }
    </style>
</head>

<body class="easyui-layout">

	<div data-options="region:'west',title:'服务编排面板'" style="width:283px;padding:1px;">
        <div class="node-panel"> </div>
	</div>
	<div data-options="region:'center',title:''" >
        <div style="text-align:center;" id="centerPanel">
            <div class="tool-panel" style="width: 100%; height: 26px;">
                <a onclick="javascript:saveNodes()" class="easyui-linkbutton" iconCls="icon-ok" plain="true">保存</a>
                <a onclick="validateNodes()"  class="easyui-linkbutton" iconCls="icon-ok" plain="true">校验</a>
                <a  class="easyui-linkbutton" iconCls="icon-ok" plain="true">预览</a>
                <span style="color: #40a0ff">模型状态：<span id="validMsg">未校验</span> </span>
            </div>
            <div id="container" ></div>
        </div>
	</div>

    
    <script>
        var appId = $.getUrlParam('appId');
        var externalData, flow, currentNode;
        var appModelId = $.getUrlParam('appModelId');
        $(function () {
            $.restfulGet('../../api/app/model/info/' + appModelId ,null,function(data){
                var appModelName = data.rows.appModelName;
                $('title').text(appModelName);
                $('#title').text(appModelName);
            });
            var fixWindowHeight = function(){
                var h = $(window).height();
                var w = $(window).width();
                flow.container.stage.height(h - 38 -35);
                flow.container.stage.width(w- 283 - 30);
            };
            $(window).on('resize', fixWindowHeight);
            $.get('../../api/app/model/info/external/' + appModelId + '/' + appId, null,
                function (data) {
                    externalData = data.rows;
                    initFlowchart();
                    fixWindowHeight();
                }, "json");

            // ctrl + s save
            window.addEventListener("keydown", function(event) {
                if (!(event.which == 83 && event.ctrlKey)) return true;
                event.preventDefault();
                saveNodes();
                return false;
            });
        });

        function openInnerServiceNode(event, node) {
            if (node && node.$data) {
                currentNode = node;
                if (currentNode.$data.nodeRefModelId) {
                    JLEUtil.createModalWin(`${node.$data.text}调用赋值`, `AppModelInfo-Node.html?appId=${appId}&nodeId=${node.$data.nodeId}&readonly=false`);
                } else {
                    //currentNode.$data.nodeId = node.id();
                    currentNode.$data.appModelId = appModelId;
                    currentNode.$data.nodeType =  currentNode.$data.type;
                    JLEUtil.createModalWin('选择内部服务', 
                        `popAppModelInfoInnerList.html?dialog=show&appId=${appId}&nodeId=${node.$data.nodeId}&readonly=false`);

                }
            }
        }

        function openInoutList(event, node) {
            if (node && node.$data) {
                currentNode = node;
                JLEUtil.createModalWin(`${node.$data.text}列表信息`, 
                    `OsrvInoutInfoList.html?dialog=show&appId=${appId}&appModelId=${appModelId}&nodeId=${node.$data.nodeId}&nodeType=${encodeURI(node.$data.type)}&readonly=false`);
            }
        }

        function openValidateList(event, node) {
            if (node && node.$data) {
                currentNode = node;
                if (!node.$data.nodeId) {
                    currentNode.$data.nodeId = node.id();
                    currentNode.$data.appModelId = appModelId;
                    currentNode.$data.nodeType =  currentNode.$data.type;
                    onChooseInnerService({appModelName: node.$data.text})
                }
                JLEUtil.createModalWin(`${node.$data.text}列表信息`, 
                    `OsrvValidateInfoList.html?dialog=show&appId=${appId}&appModelId=${appModelId}&nodeId=${node.$data.nodeId}&nodeType=${encodeURI(node.$data.type)}&readonly=false`);
            }
        }

        function openTranList(event, node) {
            if (node && node.$data) {
                currentNode = node;
                if (!node.$data.nodeId) {
                    currentNode.$data.nodeId = node.id();
                    currentNode.$data.appModelId = appModelId;
                    currentNode.$data.nodeType =  currentNode.$data.type;
                    onChooseInnerService({appModelName: node.$data.text})
                }
                JLEUtil.createModalWin(`${node.$data.text}列表信息`, 
                    `OsrvTranInfoList.html?dialog=show&appId=${appId}&appModelId=${appModelId}&nodeId=${node.$data.nodeId}&nodeType=${encodeURI(node.$data.type)}&readonly=false`);
            }
        }
        // 选择内部服务回调
        function onChooseInnerService(row) {
            var nodeData = {
                nodeId: currentNode.id(),
                nodeCode : currentNode.$data.nodeId,
                nodeRefModelId : row.appModelId,
                nodename : row.appModelName,
                text : row.appModelName,
            };
            updateNodeAfterSave(nodeData);
            nodeData = {
                nodeId : currentNode.$data.nodeId,
                nodeCode : currentNode.$data.nodeCode,
                nodeName : currentNode.$data.nodeName,
                nodeType : currentNode.$data.nodeType,
                nodeRefModelId : currentNode.$data.nodeRefModelId,
                appModelId : currentNode.$data.appModelId,
                x : currentNode.x(),
                y : currentNode.y(),
                h : currentNode.$data.h,
                w : currentNode.$data.w
            };
            $.restfulPut('../../api/osrv/node/info' , nodeData, function (data) {
                //window.parent.reloadList();
                //$.messager.alert('提示', '保存成功', 'info');
            });
            
        }

        // 退格键事件处理器
        function onDeleteKeyup(event, nodes, removeCallback) {
            if (nodes && nodes.length > 0) {
                $.messager.confirm('删除提示', '你确定要删除这个节点?', function (r) {
                    if (r) {
                        nodes.forEach(node => {
                            if (node.getClassName() == "Group") {
                                var jsonData = JSON.parse('{'+'"nodeId":"' + node.$data.nodeId + '"'+'}');
                                $.restfulDelete('../../api/osrv/node/info', jsonData, function(data) {
                                    removeCallback(nodes);
                                });
                            } else if (node.getClassName() == "Arrow") {
                                var jsonData = JSON.parse('{'+'"linkedId":"' + node.attrs.id + '"'+'}');
                                $.restfulDelete('../../api/osrv/linked/info', jsonData, function(data) {
                                    removeCallback(nodes);
                                });
                            } else {
                                removeCallback(nodes);
                            }
                        });
                    }
                });
            }
        }
        // 节点链接双击事件处理
        function onArrowDblclick(event, startNode, endNode) {
            var targetLinkedId = event.target.getId();    
            if (targetLinkedId) {
                JLEUtil.createModalWin('外部模型节点链接条件列表', `OsrvLinkedCondList.html?dialog=show&appId=${appId}&linkedId=${targetLinkedId}&readonly=false`);
            }   
        }

        // 初始化流程图
        function initFlowchart() {
            var panelNodes = [{
                    type: "00",
                    text: "外服入参",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onDblclick: openInoutList,
                    onTextChange: changeNodeTextHandler,
                    // onDragstart: function (event, node) {},
                    // onDragmove: function (event, node) {},
                    // onDragend: function (event, node) {},
                    // onSelected: function (event, node) {}
                }, {
                    type: "01",
                    text: "数据校验",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onDblclick: openValidateList,
                    onTextChange: changeNodeTextHandler,
                    // onDragstart: function (event, node) {},
                    // onDragmove: function (event, node) {},
                    // onDragend: function (event, node) {},
                    // onSelected: function (event, node) {}
                }, {
                    type: "02",
                    text: "数据赋值",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onDblclick: openTranList,
                    onTextChange: changeNodeTextHandler,
                    // onDragstart: function (event, node) {},
                    // onDragmove: function (event, node) {},
                    // onDragend: function (event, node) {},
                    // onSelected: function (event, node) {}
                }, {
                    type: "03",
                    text: "内部服务",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onTextChange: changeNodeTextHandler,
                    onDblclick: openInnerServiceNode,
                    // onDragstart: function (event, node) {},
                    // onDragmove: function (event, node) {},
                    // onDragend: function (event, node) {},
                    // onSelected: function (event, node) {}
                },  {
                    type: "04",
                    text: "条件分支",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onTextChange: changeNodeTextHandler,
                    // onDblclick: openInnerServiceNode,
                }, {
                    type: "05",
                    text: "分布式事务开始",
                    // onClick: function (event, node) {},
                    onTextChange: changeNodeTextHandler,
                    // onDblclick: openInnerServiceNode,
                }, {
                    type: "06",
                    text: "分布式事务提交",
                    // onClick: function (event, node) {},
                    onTextChange: changeNodeTextHandler,
                    // onDblclick: openInnerServiceNode,
                }, {
                    type: "07",
                    text: "分布式事务回滚",
                    // onClick: function (event, node) {},
                    onTextChange: changeNodeTextHandler,
                    // onDblclick: openInnerServiceNode,
                }, {
                    type: "99",
                    text: "外服出参",
                    color: "#f6f7ff", // konva fill
                    borderColor: "#03689a", // konva stroke
                    // onClick: function (event, node) {},
                    onDblclick: openInoutList,
                    onTextChange: changeNodeTextHandler,
                    // onDragstart: function (event, node) {},
                    // onDragmove: function (event, node) {},
                    // onDragend: function (event, node) {},
                    // onSelected: function (event, node) {}
                }, ];
            // add nodepanel div
            panelNodes.forEach( (node, index)=>{
                var nodeDOM = $(`<div class="node-drop" draggable="true">${node.text}</div>`);
                $(nodeDOM).data('nodeData', node)
                $(nodeDOM).on('dragstart', function(e){
                    window.$$FLOWCHART_NODE_DATA = $(e.target).data('nodeData')
                })
                $('.node-panel').append(nodeDOM);
            });
            var h = $(window).height();
            var w = $(window).width();
            h = h - 38 -35;
            w = w- 283 - 30;
            flow = new Flowchart({
                hidePanel: true,
                height : h,
                width : w,
                container: "container",
                onDeleteKeyup: onDeleteKeyup,
                onArrowDblclick: onArrowDblclick,
                panel: {
                    nodes: panelNodes
                }
            });
            if (!externalData) {
                return;
            }
            var osrvNodeInfos = externalData.OsrvNodeInfo || [];
            var osrvLinkedInfos = externalData.OsrvLinkedInfo || [];
            var nodes = [],
                links = [];
            osrvNodeInfos.forEach((element, index) => {
                var node = flow.factory("Node", {
                    x: element.x,
                    y: element.y,
                    id: element.nodeId,
                    type: element.nodeType,
                    text: element.nodeName,
                    "nodeId": element.nodeId,
                    "nodeCode": element.nodeCode,
                    "nodeName": element.nodeName,
                    "nodeType": element.nodeType,
                    "nodeRefModelId": element.nodeRefModelId,
                    "appModelId": element.appModelId || appModelId,
                });
                nodes.push(node);
            });
            osrvLinkedInfos.forEach(element => {
                var link = flow.factory("Link", {
                    id: element.linkedId,
                    points: [element.x1, element.y1, element.x2, element.y2],
                    startNode: element.sourceNodeId,
                    endNode: element.targetNodeId,
                    "linkedId": element.linkedId,
                    "linkedType": element.linkedType,
                    "sourceNodeId": element.sourceNodeId,
                    "targetNodeId": element.targetNodeId,
                    "linkedDesc": element.linkedDesc,
                    "x1": element.x1,
                    "y1": element.y1,
                    "x2": element.x2,
                    "y2": element.y2
                });
                links.push(link);
            });
            flow.load({
                nodes: nodes,
                links: links
            });
        }

        function closeForm() {
            var appModelInfo = externalData.AppModelInfo;
            var title = `${appModelInfo.appModelCode}-${appModelInfo.appModelName}修改`;
            window.parent.closeTab(title);
        }

        function updateNodeAfterSave(nodeData) {
            // nodeRefModelId nodename
            if (currentNode) {
                currentNode.$data.nodeId = nodeData.nodeId;
                currentNode.$data.nodeRefModelId = nodeData.nodeRefModelId;
                currentNode.$data.nodeCode = nodeData.nodeCode;
                currentNode.$data.nodeName = nodeData.nodename;
                currentNode.$data.text = nodeData.text;
                var nodeText = currentNode.findOne('.nodeText');
                nodeText.text(nodeData.nodename);
                currentNode.$data.x = currentNode.x();
                currentNode.$data.y = currentNode.y();
                currentNode.$data.h = nodeText.height();
                currentNode.$data.w = nodeText.width();
                flow.refresh();
            }
        }

        function changeNodeTextHandler(event, node, newText, oldText, callback) {
            if (newText != oldText) {
                let nodeData = {
                    nodeId : node.$data.nodeId,
                    nodeName : newText,
                    appModelId : appModelId,
                };
                $.restfulPut('../../api/osrv/node/info', nodeData, function (data) {
                    callback(newText);
                }, function(){
                    $.messager.alert('提示', '修改节点名称失败', 'error');
                });
            }
        }

        // 保存流程信息
        function saveNodes() {
            var flowData = flow.export();
            var nodes = flowData.nodes.map( el => {
                return {
                    nodeId : el.$data.nodeId || el.attrs.id,
                    nodeCode : el.$data.nodeCode,
                    nodeName : el.$data.nodeName || el.$data.text,
                    nodeType : el.$data.nodeType || el.$data.type,
                    nodeRefModelId : el.$data.nodeRefModelId,
                    appModelId : el.$data.appModelId || appModelId,
                    x : el.attrs.x,
                    y : el.attrs.y,
                    h : el.$data.h,
                    w : el.$data.w
                }
            });
            var links = flowData.links.map( el=>{
                return {
                    linkedId : el.attrs.id,
                    sourceNodeId : el.startNode,
                    targetNodeId : el.endNode,
                    x1 : el.attrs.points[0],
                    y1 : el.attrs.points[1],
                    x2 : el.attrs.points[2],
                    y2 : el.attrs.points[3]
                }
            });
            $.restfulPostJson(`../../api/app/model/info/external/${appId}`, { OsrvNodeInfo: nodes, OsrvLinkedInfo: links},
            function(data){
                if (data.success) {
                    $.messager.alert('提示', '保存成功', 'info');
                } else {
                    $.messager.alert('提示', data.message, 'error');
                }
            }, 
            function(data){
                $.messager.alert('提示', '保存失败 ' + data.message, 'error');
            });
        }

        /**
         * 一个外服模型需要外服入参，外服出参两个标签，
         * 另外还需要校验如果一个组件未跟任何其它的组件有链接则也有有问题，
         * 这两个校验出来的问题均需要在点击编辑器中的“校验“时，给出提示
         */
        function validateNodes() {
            let flowData = flow.export();
            // 判断是否有输入输出节点
            const inputNode = flowData.nodes.find(function(el){
                const type = (el.$data.nodeType || el.$data.type)
                return type == "00"
            })
            if (!inputNode) {
                $.messager.alert('提示', '缺少外服入参', 'error');
                $('#validMsg').text('缺少外服入参')
                return
            }
            const outputNode = flowData.nodes.find(function(el){
                const type = (el.$data.nodeType || el.$data.type)
                return type == "99"
            })
            if (!outputNode) {
                $.messager.alert('提示', '缺少外服出参', 'error');
                $('#validMsg').text('缺少外服出参')
                return
            }
            // 链接头尾节点映射
            let sourceLinkMap = {}, targetLinkMap = {}
            flowData.links.forEach(function(el, index){
                sourceLinkMap[el.startNode] = el
                targetLinkMap[el.endNode] = el
            })
            let linkCheck = true, wrongNode = null
            for(let i = 0; i < flowData.nodes.length; i++){
                const el = flowData.nodes[i]
                const id = (el.$data.nodeId || el.attrs.id)
                if (!(sourceLinkMap[id] || targetLinkMap[id])) {
                    linkCheck = false
                    wrongNode = el
                    break;
                }
            }
            if (!linkCheck) {
                const msg = (wrongNode.$data.nodeName || wrongNode.$data.text) + '未链接其他节点'
                $.messager.alert('提示', msg, 'error');
                $('#validMsg').text(msg)
                return
            }

            $('#validMsg').text('已校验')
        }
    </script>
</body>

</html>